<?php

require_once 'lib/NdaRequestHandler.php';

function handle_resource ()
{
	global $config;

	$tpl = tpl_engine();
	$tpl->pushResourceDir(url_dirname(__FILE__));

	$rh = new ResourceRequestHandler($config);
	$rh->tpl = $tpl;
	$rh->atf['Default']['get'] = array($rh, 'goGet');
	$rh->atf['Default']['config'] = array($rh, 'goConfig');
	$rh->atf['Config']['get'] = array($rh, 'goConfig');
	$rh->ttm['Default'] = 'resource.html';

	if ($rh->init()) return call_user_func($rh->dispatch());

	return;
} // end of function handle_about

class ResourceRequestHandler extends NdaRequestHandler
{
	public function goGet ()
	{
		$rpc = new ResourcePermissionChecker(array('base_dir' => $this->config['dir.install']));
		if ($rpc->process()) {
			$this->message = 'Все спокойно!';
		} else {
			$this->message = 'Сайту капец';
			$this->errors = $rpc->errors;
		}
	} // end of method goRefresh
}


class TooManyErrorsException extends Exception {}

/**
 * ResourcePermissionChecker проверялка прав на файлы сайта.
 *
 * @author lucky
 */
class ResourcePermissionChecker
{
	var $static_dir_perms = 0755;
	var $static_file_perms = 0644;
	var $script_dir_perms = 0755;
	var $script_file_perms = 0644;
	var $htaccess_perms = 0644;

	var $log = array();
	var $errors = array();

	var $errors_limit = 50;

	public function __construct ($config=array())
	{
		foreach ($config as $k=>$v) $this->$k = $v;
		$this->config = $config;
	} // end of __construct

	public function process ()
	{
		try {
			$this->maybeMkDir($this->base_dir.'data/', $this->static_dir_perms);
			$this->maybeMkDir($this->base_dir.'data/thumbs/', $this->static_dir_perms);

			$this->maybeMkDir($this->base_dir.'var/backup/', $this->script_dir_perms);
			$this->maybeMkDir($this->base_dir.'var/cache/', $this->script_dir_perms);
			$this->maybeMkDir($this->base_dir.'var/store/', $this->script_dir_perms);

			// проверить что в /data/ static
			$entries = $this->find($this->base_dir.'data/', array('type'=>'d'), array($this, 'checkStaticDir'));
			$entries = $this->find($this->base_dir.'data/', array('type'=>'f', 'exclude' => '*/.htaccess'), array($this, 'checkStaticFile'));

			// проверить что в /skins/ *.* static, кроме .*html которые суть скипты
			$entries = $this->find($this->base_dir.'skins/', array('type'=>'d'), array($this, 'checkStaticDir'));
			$entries = $this->find($this->base_dir.'skins/', array('type'=>'f', 'exclude' => '*.html'), array($this, 'checkStaticFile'));
			$entries = $this->find($this->base_dir.'skins/', array('type'=>'f', 'include' => '*.html'), array($this, 'checkScriptFile'));

			// проверить что в /var/ *.* scripts
			$entries = $this->find($this->base_dir.'var/', array('type'=>'f'), array($this, 'checkScriptFile'));

			// проверить что в /*.php scripts
			$entries = $this->find($this->base_dir, array('type'=>'f', 'include' => '*.php'), array($this, 'checkScriptFile'));

			// проверить что в .htaccess htaccess
			$entries = $this->find($this->base_dir, array('type'=>'f', 'include' => '.htaccess'), array($this, 'checkHtaccess'));

			$status = true;
		} catch (Exception $e) {
			$status = false;
		}
		$status &= !$this->errors;
		return $status;
	} // end of method process

	// внутрениние говорилки с юзерами человеческим глосом
	public function sayDebug ($msg)
	{
		$this->log[] = $msg;
	} // end of method say
	public function sayError ($msg)
	{
		$this->errors[] = $msg;
		if ($this->errors_limit && count($this->errors) > $this->errors_limit) {
			$this->errors[] = 'Too many errors';
			throw new TooManyErrorsException(__CLASS__);
		}
	} // end of method say

	// обновлялки прав
	public function chmod ($path, $new_perms)
	{
		if (chmod($path, $new_perms)) {
			$this->sayDebug(sprintf('Set permissions %o for %s', $new_perms, $path));
		} else {
			$this->sayError(sprintf('Can\'t set permissions %o for %s', $new_perms, $path));
		}
	} // end of method chmod
	public function mkdir (/* args to php mkdir function */)
	{
		$args = func_get_args();
    $args[0] = rtrim($args[0], '/'); // HACK : 2008-06-05 : lucky
                                     // fix bug on freebsd:
                                     // it refuses directory creation if target ends by '/'
                                     // (for example '/bin/')
		return call_user_func_array('mkdir', $args);
	} // end of method mkdir
	public function maybeChmod ($path, $required_perms)
	{
		$perms = fileperms($path) & 0777;
		if ($perms !== $required_perms) {
			$this->chmod($path, $required_perms);
		}
	} // end of method chmod
	public function maybeMkDir ($path, $required_perms)
	{
		if (!is_dir($path)) {
			if (!file_exists($path)) {
				if ($this->mkdir($path, $required_perms, true)) {
					$this->sayDebug(sprintf("Create directory %s with perms %o", $path, $required_perms));
				} else {
					$this->sayError(sprintf("Can't create directory %s with perms %o", $path, $required_perms));
				}
			} else {
				$this->sayError(sprintf("Can't create directory %s: file exists.", $path, $required_perms));
			}
		}
	} // end of method chmod

	// проверялки прав на ресурсы разных типов
	public function checkStaticDir ($path)
	{
		$this->maybeChmod($path, $this->static_dir_perms);
	} // end of method checkStaticFile
	public function checkStaticFile ($path)
	{
		$this->maybeChmod($path, $this->static_file_perms);
	} // end of method checkStaticFile
	public function checkScriptFile ($path)
	{
		$this->maybeChmod($path, $this->script_file_perms);
	} // end of method checkStaticFile
	public function checkHtaccess ($path)
	{
		$this->maybeChmod($path, $this->htaccess_perms);
	} // end of method checkStaticFile

	// искалки файлов
	public function find ($path, $filter, $action=NULL)
	{
		$res = array();

		if ($this->find_match($path, $filter)) {
			if (isset($action)) call_user_func($action, $path);
			else $res[] = $path;
		}
		if (is_dir($path) && is_readable($path) && ($d = dir($path))) {
			while (false !== ($entry = $d->read())) {
				if ($entry !== '.' && $entry !== '..') {
					$res = array_merge($res, $this->find($d->path . '/'. $entry, $filter, $action));
				}
			}
			$d->close();
		}
		return $res;
	} // end of method find
	public function find_match ($path, $filter)
	{
		$status = true;
		if ($status && isset($filter['type'])) {
			switch ($filter['type']) {
			case 'd': $status &= is_dir($path); break;
			case 'f': $status &= is_file($path); break;
			default: $status = false; // unknown entry type
			}
		}
		if ($status && isset($filter['include'])) {
			$status &= fnmatch($filter['include'], $path);
		}
		if ($status && isset($filter['exclude'])) {
			$status &= !fnmatch($filter['exclude'], $path);
		}
		return $status;
	} // end of method find_match
}
